package cn.iocoder.yudao.framework.business.basic.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.business.basic.dao.mapper.CoreMapper;
import cn.iocoder.yudao.framework.business.basic.dao.query.CoreQueryWrapper;
import cn.iocoder.yudao.framework.business.basic.pojo.po.MemberScopeEntity;
import cn.iocoder.yudao.framework.business.basic.service.ChainWrapperService;
import cn.iocoder.yudao.framework.business.basic.service.SimpleDetailService;
import cn.iocoder.yudao.framework.business.basic.service.ValidateService;
import cn.iocoder.yudao.framework.common.exception.Assertions;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.constant.ErrorCodeConstants;
import cn.iocoder.yudao.framework.util.MyBatisPlusUtils;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.UpdateChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.toolkit.ChainWrappers;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import jakarta.annotation.Nullable;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.GenericTypeResolver;

import java.util.ArrayList;
import java.util.List;

/**
 * <pre>
 * OOoO0OOoO0OOOooo0oOOOO0OOOOO0oooOO0ooOOO0Ooooo0OOOOo0ooooO0OOooo0Ooooo0OOOOO
 *  基础服务
 * OOoO0OOoO0OOOooo0oOOOO0OOOOO0oooOO0ooOOO0Ooooo0OOOOo0ooooO0OOooo0Ooooo0OOOOO
 * </pre>
 *
 * @author 山野羡民（1032694760@qq.com）
 * @since 2025/01/10
 * @see com.baomidou.mybatisplus.extension.service.impl.ServiceImpl
 */
@Slf4j
public abstract class AbstractService<ENTITY, VO> implements SimpleDetailService<VO>, ValidateService<ENTITY>, ChainWrapperService<ENTITY> {
    private Class<ENTITY> entityClass;
    private Class<VO> viewObjectClass;

    public VO convertToViewObject(ENTITY entity) {
        if (entity == null) {
            return null;
        }
        return BeanUtils.toBean(entity, resolveViewObjectClass());
    }

    public List<VO> convertToViewObject(List<ENTITY> entities) {
        if (CollUtil.isEmpty(entities)) {
            return new ArrayList<>();
        }
        return entities.stream().map(this::convertToViewObject).toList();
    }

    public PageResult<VO> convertToViewObject(PageResult<ENTITY> page) {
        if (page == null || CollUtil.isEmpty(page.getList())) {
            return PageResult.empty();
        }
        PageResult<VO> result = new PageResult<>();
        result.setTotal(page.getTotal());
        result.setList(convertToViewObject(page.getList()));
        return result;
    }

    @Override
    public ENTITY validateExists(Long id, @Nullable Long userId) {
        ENTITY entity = selectEntityById(id);
        Assertions.notNull(entity, ErrorCodeConstants.DATA_NOT_EXISTS.getMsg());
        if (userId != null && userId > 0 && entity instanceof MemberScopeEntity aDo) {
            Assertions.owner(aDo.getUserId(), userId);
        }
        return entity;
    }

    @Override
    public <P> boolean checkExists(P param, @Nullable Long userId) {
        return false;
    }

    @Override
    public VO getDetail(Long id, @Nullable Long userId) {
        ENTITY entity = selectEntityById(id);
        if (entity == null) {
            return null;
        }
        if (entity instanceof MemberScopeEntity memberScopeEntity) {
            Assertions.owner(memberScopeEntity.getUserId(), userId);
        }
        return convertToViewObject(entity);
    }

    protected ENTITY selectEntityById(Long id) {
        CoreQueryWrapper<ENTITY> wrapper = new CoreQueryWrapper<>();
        optimizeSelectWrapper(wrapper, id);
        return getInjectedMapper().selectOne(wrapper);
    }

    public List<VO> getList(Wrapper<ENTITY> wrapper) {
        optimizeSelectWrapper(wrapper, null);
        List<ENTITY> list = getInjectedMapper().selectList(wrapper);
        if (list == null || list.isEmpty()) {
            return CollUtil.newArrayList();
        }
        return convertToViewObject(list);
    }

    public PageResult<VO> getPage(Integer current, Integer size, Wrapper<ENTITY> wrapper) {
        if (current == null) {
            current = 1;
        }
        if (size == null) {
            size = 10;
        }
        optimizeSelectWrapper(wrapper, null);
        Page<ENTITY> page = getInjectedMapper().selectPage(new Page<>(current, size), wrapper);
        List<ENTITY> records = page.getRecords();
        if (records == null || records.isEmpty()) {
            return PageResult.empty();
        }
        return new PageResult<>(convertToViewObject(records), page.getTotal());
    }

    protected void optimizeSelectWrapper(Wrapper<ENTITY> wrapper, Long id) {
        if (wrapper == null) {
            wrapper = new CoreQueryWrapper<>();
        }
        Class<ENTITY> entityClass = resolveEntityClass();
        Class<VO> voClass = resolveViewObjectClass();
        if (entityClass == null || voClass == null) {
            return;
        }
        TableInfo tableInfo = MyBatisPlusUtils.newOrGetTableInfo(entityClass);
        // 默认基于视图对象的 @DataSelectable 注解提取最小集 SELECT 字段，只查询视图对象中声明的字段
        if (wrapper instanceof QueryWrapper<ENTITY> queryWrapper) {
            if (id != null && id > 0) {
                queryWrapper.eq(tableInfo.getKeyColumn(), id);
            }
            MyBatisPlusUtils.optimizeSelect(queryWrapper, entityClass, voClass);
        } else if (wrapper instanceof LambdaQueryWrapper<ENTITY> queryWrapper) {
            if (id != null && id > 0) {
                queryWrapper.apply(tableInfo.getKeyColumn() + "=" + id);
            }
            MyBatisPlusUtils.optimizeSelect(queryWrapper, entityClass, voClass);
        } else if (wrapper instanceof MPJLambdaWrapper<ENTITY> lambdaWrapper) {
            if (id != null && id > 0) {
                lambdaWrapper.eq(tableInfo.getKeyColumn(), id);
            }
            MyBatisPlusUtils.optimizeSelect(lambdaWrapper, entityClass, voClass);
        }
        // 默认按主键降序排序
        if (!StrUtil.containsIgnoreCase(wrapper.getSqlSegment(), "ORDER BY") && tableInfo.havePK()) {
            if (wrapper instanceof QueryWrapper<ENTITY> queryWrapper) {
                queryWrapper.orderByDesc(tableInfo.getKeyColumn());
            } else if (wrapper instanceof LambdaQueryWrapper<ENTITY> queryWrapper) {
                queryWrapper.last("ORDER BY " + tableInfo.getKeyColumn() + " DESC");
            } else if (wrapper instanceof MPJLambdaWrapper<ENTITY> lambdaWrapper) {
                lambdaWrapper.orderByDesc(tableInfo.getKeyColumn());
            }
        }
    }

    @Override
    public QueryChainWrapper<ENTITY> query() {
        return ChainWrappers.queryChain(getInjectedMapper());
    }

    @Override
    public LambdaQueryChainWrapper<ENTITY> lambdaQuery() {
        return ChainWrappers.lambdaQueryChain(getInjectedMapper());
    }

    @Override
    public UpdateChainWrapper<ENTITY> update() {
        return ChainWrappers.updateChain(getInjectedMapper());
    }

    @Override
    public LambdaUpdateChainWrapper<ENTITY> lambdaUpdate() {
        return ChainWrappers.lambdaUpdateChain(getInjectedMapper());
    }

    /**
     * 反射获取响应类，建议最终子类重写该方法明确指定，避免反射带来的性能问题
     */
    protected Class<ENTITY> resolveEntityClass() {
        try {
            if (entityClass == null) {
                Class<?>[] classes = GenericTypeResolver.resolveTypeArguments(getClass(), AbstractService.class);
                assert classes != null && classes.length > 0;
                //noinspection unchecked
                entityClass = (Class<ENTITY>) classes[0];
            }
        } catch (Exception e) {
            log.error("数据实体类获取失败", e);
        }
        return entityClass;
    }

    /**
     * 反射获取响应类，建议最终子类重写该方法明确指定，避免反射带来的性能问题
     */
    protected Class<VO> resolveViewObjectClass() {
        try {
            if (viewObjectClass == null) {
                Class<?>[] classes = GenericTypeResolver.resolveTypeArguments(getClass(), AbstractService.class);
                assert classes != null && classes.length > 1;
                //noinspection unchecked
                viewObjectClass = (Class<VO>) classes[1];
            }
        } catch (Exception e) {
            log.error("视图对象类获取失败", e);
        }
        return viewObjectClass;
    }

    /**
     * 【避坑】父类的 Mapper 得用泛型，不能直接用 BaseMapper 接口，因为该接口有多个实现类，会报如下错误：
     * Parameter 0 of constructor in xxxServiceImpl required a single bean, but 3 were found: xxx
     * 【避坑】父类的 Mapper 不能使用 @Autowired 或 @Resource 注解注入，会报错：A component required a single bean, but 6 were found: xxx
     */
    protected abstract CoreMapper<ENTITY> getInjectedMapper();

}
